﻿using System;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace PolicyServer
{
    public class SL_SocketPortPolicyListener
    {
        private const string _PolicyRequestString = "<policy-file-request/>";
        private int _ReceivedLength;
        private byte[] _Policy;
        private byte[] _ReceiveBuffer;
        private TcpListener _Listener;
        private TcpClient _Client;
        private EventLog eventLog;

        private static readonly SL_SocketPortPolicyListener _singleton = new SL_SocketPortPolicyListener();

        public static SL_SocketPortPolicyListener Singleton
        {
            get
            {
                return _singleton;
            }
        }

        private SL_SocketPortPolicyListener(EventLog serviceEventLog)
        {
            eventLog = serviceEventLog;
        }

        private SL_SocketPortPolicyListener()
            : this(new EventLog("Application"))
        {
        }

        public void Start()
        {
            try
            {
                //增加的代码
                var policyConfig =
                    "<?xml version=\"1.0\" encoding =\"utf-8\"?>" +
                        "<access-policy>" +
                          "<cross-domain-access>" +
                            "<policy>" +
                              "<allow-from>" +
                                "<domain uri=\"*\" />" +
                              "</allow-from>" +
                              "<grant-to>" +
                                "<socket-resource port=\"4502-4530\" protocol=\"tcp\" />" +
                              "</grant-to>" +
                            "</policy>" +
                          "</cross-domain-access>" +
                        "</access-policy>";

                //增加的代码
                _Policy = Encoding.Default.GetBytes(policyConfig);
                _ReceiveBuffer = new byte[_PolicyRequestString.Length];

                //Using TcpListener which is a wrapper around a Socket
                //Allowed port is 943 for Silverlight sockets policy data
                _Listener = new TcpListener(IPAddress.Any, 943);
                _Listener.Start();
                _Listener.BeginAcceptTcpClient(new AsyncCallback(OnBeginAccept), null);
            }
            catch (Exception exp)
            {
                LogError(exp);
            }
        }

        private void OnBeginAccept(IAsyncResult ar)
        {
            _Client = _Listener.EndAcceptTcpClient(ar);
            _Client.Client.BeginReceive(_ReceiveBuffer, 0, _PolicyRequestString.Length, SocketFlags.None,
                new AsyncCallback(OnReceiveComplete), null);
        }

        private void OnReceiveComplete(IAsyncResult ar)
        {
            try
            {
                _ReceivedLength += _Client.Client.EndReceive(ar);
                if (_ReceivedLength < _PolicyRequestString.Length)
                {
                    _Client.Client.BeginReceive(_ReceiveBuffer, _ReceivedLength,
                        _PolicyRequestString.Length - _ReceivedLength,
                        SocketFlags.None, new AsyncCallback(OnReceiveComplete), null);
                    return;
                }

                string request = System.Text.Encoding.UTF8.GetString(_ReceiveBuffer, 0, _ReceivedLength);
                if (StringComparer.InvariantCultureIgnoreCase.Compare(request, _PolicyRequestString) != 0)
                {
                    _Client.Client.Close();
                    return;
                }
                _Client.Client.BeginSend(_Policy, 0, _Policy.Length, SocketFlags.None,
                    new AsyncCallback(OnSendComplete), _Client.Client);
            }
            catch (Exception exp)
            {
                _Client.Client.Close();
                LogError(exp);
            }
            _ReceivedLength = 0;
            _Listener.BeginAcceptTcpClient(new AsyncCallback(OnBeginAccept), null);
        }

        private void OnSendComplete(IAsyncResult ar)
        {
            Socket socket = (Socket)ar.AsyncState;
            try
            {
                socket.EndSend(ar);
            }
            catch (Exception exp)
            {
                LogError(exp);
            }
            finally
            {
                socket.Close();
            }
        }
        private void LogError(Exception exp)
        {
            eventLog.WriteEntry(string.Format("Error in PolicySocketServer: {0} \r\n StackTrace: {1}", exp.Message, exp.StackTrace));
        }
    }
}
